JS Promise, sync await 实现的 jsonp 方案, json 转换


/**

  • @description 模仿信号机制
  • // function A
  • const demoSignal = createSignal(1000); // currentTime: 1552641398505
  • await doSomething();
  • await doSomethingElse();
  • demoSignal.fulfill('hahah'); // currentTime: 1552641413819
  • // function B
  • const res = await demoSignal; // currentTime: 1552641398505
  • console.log(res, Date.now()); // print 'haha, 1552641413819'
  • @param {Number} timeout 超时时间,超过时间自动resolve
  • @return {Signal} 信号 extends Promise fulfill
    */
    exports.createSignal = function(timeout = 4000) {
    let resolver;
    const signal = new Promise(resolve => {
    resolver = resolve;
    });
    signal.fulfill = resolver;
    if (timeout) {
    setTimeout(() => signal.fulfill(), timeout);
    }
    return signal;
    };

使用
1、创建信号量promise ctx.dingqiMarketQiangGouSignal = createSignal();
2、其他地方需要等待这个信号量 就 const qiangGouDelData = ctx.dingqiMarketQiangGouSignal ? await ctx.dingqiMarketQiangGouSignal : [];
3、最后原始信号量执行resolve ctx.dingqiMarketQiangGouSignal.fulfill(fulfillArray); 原等待的地方 获得数据
总结:
promise 遇到await 的时候一定要等到 resolve 执行之后才会执行下面的逻辑;
例如
setTimeout( function () {
console.log( 'setTimeout' )
}, 0 );
let a;
const b = new Promise( function ( resolve ) {
console.log( 'promise1' )
a = resolve;
} ).then( function () {
console.log( 'promise2' )
} );
async function foo() {
console.log( '001' )
await b;
console.log( '002' )
}
foo();
a(); // 只有执行了 这行 002 才能打印
console.log( 'script end' );

[Log] promise1
[Log] 001
[Log] script end
[Log] promise2
[Log] 002
< undefined
[Log] setTimeout


setTimeout( function () {
console.log( 'setTimeout' )
}, 0 );
new Promise( function ( resolve ) {
console.log( 'promise1' )
resolve();
} ).then( function () {
console.log( 'promise2' )
} )
console.log( 'script end' )

[Log] promise1
[Log] script end
[Log] promise2
< undefined 打印的原因?
[Log] setTimeout


Promise 使用,Promise定义后会立马执行
async function foo() {
console.log( 'foo start' )
await bar()
console.log( 'foo end' )
}
async function bar() {
console.log( 'bar' )
}
console.log( 'script start' )
setTimeout( function () {
console.log( 'setTimeout' )
}, 0 )
foo();
const a = new Promise( function ( resolve ) {
console.log( 'promise1' )
return resolve('222');
} );
a.then( function (v) {
console.log( 'promise2'+v )
} );
console.log( 'script end' )
[Log] script start
[Log] foo start
[Log] bar
[Log] promise1
[Log] script end
[Log] promise2222
[Log] foo end
< undefined
[Log] setTimeout

解析:
对于await来说,分2个情况
不是promise对象
是promise对象

如果不是 promise , await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西,作为 await表达式的结果;

如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。

我们要先明确一些基本概念,在Js 中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列可以有多个,微任务队列只有一个。

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.

  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver.

下面来根据线程说下具体的执行顺序,我们都知道js是单线程的,他在执行的时候把各种任务放在队列里,会依此执行,但是根据以上的信息,我们先把任务流模拟下:

首先是2个函数声明,虽然有async关键字,但不是调用我们就不看。然后首先是打印同步代码 console.log('script start');//这是全局script任务,作为宏任务1

然后将将setTimeout放入宏任务队列,这里是宏任务2

调用foo,打印 同步代码 console.log( foo start' );

接着 await async2(),我们来分析下它做了什么:

  1. 先得到await右侧表达式的结果。执行async2(),打印同步代码console.log('bar'), 参考上面的结论,这时候是进程被阻塞的,被阻塞后,要执行async之外的代码的,输出promise1,??这里只是把promise2推入微任务队列,并没有执行。微任务会在当前宏任务的同步代码执行完毕,才会依次执行,然后foo end,最后return Promise.resolve(undefined);

  2. 对于 await Promise.resolve(undefined) 如何理解呢?

    根据 MDN 原话我们知道

    如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。

    在我们这个例子中,就是Promise.resolve(undefined)正常处理完成,并返回其处理结果。那么await bar()就算是执行结束了。

    目前这个promise的状态是fulfilled,等其处理结果返回就可以执行await下面的代码了。

    那何时能拿到处理结果呢?

    回忆平时我们用promise,调用resolve后,何时能拿到处理结果?是不是需要在then的第一个参数里,才能拿到结果。(调用resolve时,会把then的参数推入微任务队列,等主线程空闲时,再调用它)

    所以这里的 await Promise.resolve() 就类似于:

    把then的第一个回调参数 (undefined) => {} 推入微任务队列。

    then执行完,才是await far()执行结束。

    await far()执行结束,才能继续执行后面的代码.

  3. 此时当前宏任务1都执行完了,要处理微任务队列里的代码。

微任务队列,先进先出的原则,

  • 执行微任务1,打印promise2

  • 执行微任务2,没什么内容..

但是微任务2执行后,await far()语句结束,后面的代码不再被阻塞,所以打印

console.log( 'foo end' )

等到宏任务1执行完,还有它队列里的为任务也执行完毕,就开始宏任务2,打印结果:setTimeout


;(function (root) {
/**

  • InterceptorManage, 代表一个拦截器,用于发起jsonp前及响应之后的统一操作
  • @constructor
  • @param {array} handlers - 存放数组
    /
    class InterceptorManager {
    constructor () {
    this.handlers = []
    }
    /
    *
    • 添加promise(resolve, reject)对象至数组
    • @param {function} fulfilled - promise resolve函数
    • @param {function} rejected - promise rejected函数
    • @return {number} id - 在handlers数组中的位置
      /
      use (fulfilled, rejected) {
      this.handlers.push({
      fulfilled: fulfilled,
      rejected: rejected
      })
      return this.handlers.length - 1
      }
      /
      *
    • 通过id删除handlers中的项
    • @param {number} id - use函数返回的所在handlers的下标
      */
      eject (id) {
      if (this.handlers[id]) {
      this.handlers[id] = null
      }
      }
      }

jsonp.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}

/** 解析器parser

  • parser.toJSON() 将对象转换成字符串
  • parser.toObject() 将键值对字符串转换成数组
    /
    let parser = {
    /
    *
    • 将对象转换成特定字符串格式
    • @param {object} obj - 目标对象
    • @param {string} separator - 键值对之中的分隔符,默认为'='
    • @param {string} delimiter - 键值对连接的分隔符,默认为'&'
    • @example
    • // return a=1&b=2
    • parser.toJSON({a:1, b: 2})
    • // return a=1?b=2
    • parser.toJSON({a:1, b: 2}, undefined, '?')
      /
      toJSON (obj = {}, separator = '=', delimiter = '&') {
      let result = ''
      Object.entries(obj).forEach(([key, value], index) => {
      result += ((index === 0) ? '' : delimiter) + key + separator + value
      })
      return result
      },
      /
      *
    • 将键值对字符串转换成对象
    • @param {string} jsonstr - 要转换的键值对字符串
    • @param {*} separator - 键值对之中的分隔符,默认为'='
    • @param {*} delimiter - 键值对连接的分隔符,默认为'&'
    • @example
    • // return {a: '1', b: '2'}
    • parser.toObject('a=1&b=2')
    • // return {a: '1', b: '2'}
    • parser.toObject('a:1?b:2', ':', '?')
      */
      toObject (jsonstr, separator = '=', delimiter = '&') {
      if (typeof jsonstr !== 'string') {
      throw new Error('first parameter must be a string')
      }
      return jsonstr.split(delimiter).reduce((result, item) => {
      result[item.split(separator)[0]] = item.split(separator)[1]
      return result
      }, {})
      }
      }

/** 定义空函数 */
let noop = () => {}

/**

  • promise 实现的jsonp函数
  • @param {string} url - jsonp 请求地址
  • @param {object} options - jsonp 请求参数对象,其中属性jsonpCallback 为函数名
    /
    function jsonp (url, options = {jsonpCallback: 'callback'}) {
    // 用法 Promise.resolve https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
    let promise = Promise.resolve(options)
    let dispatchRequest = function () {
    /
    * 返回promise, 内部为传统jsonp的用法及代码 */
    return new Promise((resolve, reject) => {
    let s = document.createElement('script')
    let index = url.indexOf('?')
    if (index > -1) {
    Object.assign(options, parser.toObject(url.substring(index + 1)))
    url = url.substring(0, index)
    }
    let cName = 'jsonp' + Date.now()
    options[options['jsonpCallback']] = cName
    delete options['jsonpCallback']
    window[cName] = function (res) {
    resolve(res)
    }
    s.src = url + '?' + parser.toJSON(options)
    document.body.appendChild(s)
    s.remove()
    s.onerror = function (err) {
    rejected(err)
    }
    })
    }
    let chain = [dispatchRequest, noop]
    // request dispatch前置处理
    jsonp.interceptors.request.handlers.forEach((interceptor) => {
    chain.unshift(interceptor.fulfilled, interceptor.rejected)
    })
    // response dispatch数据之后处理
    jsonp.interceptors.response.handlers.forEach((interceptor) => {
    chain.push(interceptor.fulfilled, interceptor.rejected)
    })
    while (chain.length) {
    // 循环处理 里面的所有逻辑
    promise = promise.then(chain.shift(), chain.shift())
    }
    return promise
// 第一种调用
// jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', {
//     wd: 'github',
//     jsonpCallback: 'cb'
// }).then((res) => {
//     console.log(res)
// })

// 第二种调用
//Add a request interceptor
// jsonp.interceptors.request.use((config) => {
//     // Do something before request is send
//     return config
// }, (error) => {
//     // Do something with request error
//     return Promise.reject(error)
// })
// //Add a response interceptor
// jsonp.interceptors.response.use((response) => {
//     // Do something with response data
//     return response
// }, (error) => {
//     // Do something with response error
//     return Promise.reject(error)
// })



// 示例
// var thenable = { then: function(resolve, reject) {
//   reject('444');
// //   throw new TypeError("Throwing");
// //   resolve("Resolving");
// }};
// var p2 = Promise.resolve(thenable);
// p2.then(function(v) {
//   // 不会被调用
// }, function(e) {
//   console.log(e); // TypeError: Throwing
// });

}

/** ??榛庾?*/
if (typeof define !== 'undefined' && define.amd) {
define([], function () {
return jsonp
})
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = jsonp
} else {
root.jsonp = jsonp
}
})(this)

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容