JS Promise, sync await 實現(xiàn)的 jsonp 方案讼庇, json 轉(zhuǎn)換


/**

  • @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做祝、創(chuàng)建信號量promise ctx.dingqiMarketQiangGouSignal = createSignal();
2报腔、其他地方需要等待這個信號量 就 const qiangGouDelData = ctx.dingqiMarketQiangGouSignal ? await ctx.dingqiMarketQiangGouSignal : [];
3、最后原始信號量執(zhí)行resolve ctx.dingqiMarketQiangGouSignal.fulfill(fulfillArray); 原等待的地方 獲得數(shù)據(jù)
總結(jié):
promise 遇到await 的時候一定要等到 resolve 執(zhí)行之后才會執(zhí)行下面的邏輯剖淀;
例如
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(); // 只有執(zhí)行了 這行 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定義后會立馬執(zhí)行
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會阻塞后面的代碼,先執(zhí)行async外面的同步代碼炮姨,同步代碼執(zhí)行完捌刮,再回到async內(nèi)部,把這個非promise的東西舒岸,作為 await表達式的結(jié)果绅作;

如果它等到的是一個 promise 對象,await 也會暫停async后面的代碼蛾派,先執(zhí)行async外面的同步代碼俄认,等著 Promise 對象 fulfilled,然后把 resolve 的參數(shù)作為 await 表達式的運算結(jié)果洪乍。

我們要先明確一些基本概念眯杏,在Js 中,有兩類任務隊列:宏任務隊列(macro tasks)和微任務隊列(micro tasks)壳澳。宏任務隊列可以有多個岂贩,微任務隊列只有一個。

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

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

下面來根據(jù)線程說下具體的執(zhí)行順序巷波,我們都知道js是單線程的萎津,他在執(zhí)行的時候把各種任務放在隊列里卸伞,會依此執(zhí)行,但是根據(jù)以上的信息锉屈,我們先把任務流模擬下:

首先是2個函數(shù)聲明荤傲,雖然有async關(guān)鍵字,但不是調(diào)用我們就不看部念。然后首先是打印同步代碼 console.log('script start')弃酌;//這是全局script任務,作為宏任務1

然后將將setTimeout放入宏任務隊列儡炼,這里是宏任務2

調(diào)用foo妓湘,打印 同步代碼 console.log( foo start' );

接著 await async2(),我們來分析下它做了什么:

  1. 先得到await右側(cè)表達式的結(jié)果乌询。執(zhí)行async2()榜贴,打印同步代碼console.log('bar'), 參考上面的結(jié)論,這時候是進程被阻塞的妹田,被阻塞后唬党,要執(zhí)行async之外的代碼的,輸出promise1鬼佣,??這里只是把promise2推入微任務隊列驶拱,并沒有執(zhí)行。微任務會在當前宏任務的同步代碼執(zhí)行完畢晶衷,才會依次執(zhí)行蓝纲,然后foo end,最后return Promise.resolve(undefined);

  2. 對于 await Promise.resolve(undefined) 如何理解呢晌纫?

    根據(jù) MDN 原話我們知道

    如果一個 Promise 被傳遞給一個 await 操作符税迷,await 將等待 Promise 正常處理完成并返回其處理結(jié)果。

    在我們這個例子中锹漱,就是Promise.resolve(undefined)正常處理完成箭养,并返回其處理結(jié)果。那么await bar()就算是執(zhí)行結(jié)束了哥牍。

    目前這個promise的狀態(tài)是fulfilled毕泌,等其處理結(jié)果返回就可以執(zhí)行await下面的代碼了。

    那何時能拿到處理結(jié)果呢嗅辣?

    回憶平時我們用promise懈词,調(diào)用resolve后,何時能拿到處理結(jié)果辩诞?是不是需要在then的第一個參數(shù)里坎弯,才能拿到結(jié)果。(調(diào)用resolve時,會把then的參數(shù)推入微任務隊列抠忘,等主線程空閑時撩炊,再調(diào)用它)

    所以這里的 await Promise.resolve() 就類似于:

    把then的第一個回調(diào)參數(shù) (undefined) => {} 推入微任務隊列。

    then執(zhí)行完崎脉,才是await far()執(zhí)行結(jié)束拧咳。

    await far()執(zhí)行結(jié)束,才能繼續(xù)執(zhí)行后面的代碼.

  3. 此時當前宏任務1都執(zhí)行完了囚灼,要處理微任務隊列里的代碼骆膝。

微任務隊列,先進先出的原則灶体,

  • 執(zhí)行微任務1阅签,打印promise2

  • 執(zhí)行微任務2,沒什么內(nèi)容..

但是微任務2執(zhí)行后蝎抽,await far()語句結(jié)束政钟,后面的代碼不再被阻塞,所以打印

console.log( 'foo end' )

等到宏任務1執(zhí)行完樟结,還有它隊列里的為任務也執(zhí)行完畢养交,就開始宏任務2,打印結(jié)果:setTimeout


;(function (root) {
/**

  • InterceptorManage, 代表一個攔截器瓢宦,用于發(fā)起jsonp前及響應之后的統(tǒng)一操作
  • @constructor
  • @param {array} handlers - 存放數(shù)組
    /
    class InterceptorManager {
    constructor () {
    this.handlers = []
    }
    /
    *
    • 添加promise(resolve, reject)對象至數(shù)組
    • @param {function} fulfilled - promise resolve函數(shù)
    • @param {function} rejected - promise rejected函數(shù)
    • @return {number} id - 在handlers數(shù)組中的位置
      /
      use (fulfilled, rejected) {
      this.handlers.push({
      fulfilled: fulfilled,
      rejected: rejected
      })
      return this.handlers.length - 1
      }
      /
      *
    • 通過id刪除handlers中的項
    • @param {number} id - use函數(shù)返回的所在handlers的下標
      */
      eject (id) {
      if (this.handlers[id]) {
      this.handlers[id] = null
      }
      }
      }

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

/** 解析器parser

  • parser.toJSON() 將對象轉(zhuǎn)換成字符串
  • parser.toObject() 將鍵值對字符串轉(zhuǎn)換成數(shù)組
    /
    let parser = {
    /
    *
    • 將對象轉(zhuǎn)換成特定字符串格式
    • @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
      },
      /
      *
    • 將鍵值對字符串轉(zhuǎn)換成對象
    • @param {string} jsonstr - 要轉(zhuǎn)換的鍵值對字符串
    • @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
      }, {})
      }
      }

/** 定義空函數(shù) */
let noop = () => {}

/**

  • promise 實現(xiàn)的jsonp函數(shù)
  • @param {string} url - jsonp 請求地址
  • @param {object} options - jsonp 請求參數(shù)對象,其中屬性jsonpCallback 為函數(shù)名
    /
    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疲吸, 內(nèi)部為傳統(tǒng)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數(shù)據(jù)之后處理
    jsonp.interceptors.response.handlers.forEach((interceptor) => {
    chain.push(interceptor.fulfilled, interceptor.rejected)
    })
    while (chain.length) {
    // 循環(huán)處理 里面的所有邏輯
    promise = promise.then(chain.shift(), chain.shift())
    }
    return promise
// 第一種調(diào)用
// jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', {
//     wd: 'github',
//     jsonpCallback: 'cb'
// }).then((res) => {
//     console.log(res)
// })

// 第二種調(diào)用
//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) {
//   // 不會被調(diào)用
// }, 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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市前鹅,隨后出現(xiàn)的幾起案子摘悴,更是在濱河造成了極大的恐慌,老刑警劉巖舰绘,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹂喻,死亡現(xiàn)場離奇詭異,居然都是意外死亡捂寿,警方通過查閱死者的電腦和手機口四,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秦陋,“玉大人蔓彩,你說我怎么就攤上這事。” “怎么了赤嚼?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵旷赖,是天一觀的道長。 經(jīng)常有香客問我更卒,道長等孵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任蹂空,我火速辦了婚禮俯萌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘上枕。我一直安慰自己咐熙,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布姿骏。 她就那樣靜靜地躺著糖声,像睡著了一般。 火紅的嫁衣襯著肌膚如雪分瘦。 梳的紋絲不亂的頭發(fā)上蘸泻,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音嘲玫,去河邊找鬼悦施。 笑死,一個胖子當著我的面吹牛去团,可吹牛的內(nèi)容都是我干的抡诞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼土陪,長吁一口氣:“原來是場噩夢啊……” “哼昼汗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鬼雀,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤顷窒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后源哩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鞋吉,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年励烦,在試婚紗的時候發(fā)現(xiàn)自己被綠了谓着。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡坛掠,死狀恐怖赊锚,靈堂內(nèi)的尸體忽然破棺而出治筒,到底是詐尸還是另有隱情,我是刑警寧澤改抡,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布矢炼,位于F島的核電站,受9級特大地震影響阿纤,放射性物質(zhì)發(fā)生泄漏句灌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一欠拾、第九天 我趴在偏房一處隱蔽的房頂上張望胰锌。 院中可真熱鬧,春花似錦藐窄、人聲如沸资昧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽格带。三九已至,卻和暖如春刹枉,著一層夾襖步出監(jiān)牢的瞬間叽唱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工微宝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棺亭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓蟋软,卻偏偏與公主長得像镶摘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岳守,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容